package edu.cmsc611.textsimulator;
import java.util.*;
import java.lang.*;
import java.awt.*;

import edu.cmsc611.textsimulator.Instruction.regType;
import edu.cmsc611.textsimulator.Registers.registerMode;

/**
 * Decode handles the operation for the decode stage of pipelined execution.
 */
class Decode extends Stage {
	/**
	 * holds the Rd of the last three instruction to detect hazards
	 */
	private Vector hazardList;
	/**
	 * placeholder for an instruction that is stalled.
	 */
	private Instruction instructionSave;
	/**
	 * saves the program counter for an instruction that is stalled.
	 */
	private int savePC;
	/**
	 * indicates that a stall has been issued.
	 */
	public boolean isStall;
	/**
	 * indicates that a stall has been detected.
	 */
	public boolean stallflag;
	/**
	 * indicates whether branches are assumed taken (not used).
	 */
	public boolean assumeBranchTaken;
	/**
	 * indicates whether a branch instruction was taken on its last execution.
	 */
	public boolean branchPrediction;
	/**
	 * indicates whether forwarding is used.
	 */
	public boolean forwarding;
	public boolean tableincrease=false;
	/**
	 * table used to predict branches (unused).
	 */
	public Vector branchTable;
	public int saveLastFetchInstructionopcode;

	/**
	 * initialzes the fields of the object
	 */
	/**
	 * Keep stage information to all the stage classes
	 */
	public Stages stg;
	/**
	 * keep the table of execution process
	 */
	OutputChart tablechart=null;

	Utilities utils;
	Registers reg;
	boolean RAWHazards=false;
	boolean WAWHazards=false;
	boolean WARHazards=false;
	//Fetch myFetch;
	//Execute myExec;
	//WriteBack myWriteback;

	/**
	 * keep previous instruction to check consecutive similar instruction
	 */
	public Instruction prevInstruction;

	public Decode() {
		super();
		instructionSave = new Instruction("NOP");
		isStall = false;
		assumeBranchTaken = true;   // assume branches always taken (when prediction disabled)
		/*hazardList = new Vector(3);
		branchTable = new Vector();
		hazardList.addElement(new Integer(0));
		hazardList.addElement(new Integer(0));
		hazardList.addElement(new Integer(0));*/
	}

	/**
	 * process the current instruction.  Implements the functionality of the decode stage of a pipeline.
	 * Uses a vector of instructions, each of the other pipeline stages, the register file, and the
	 * ListBoxes object.
	 * @see Instruction
	 * @see Fetch
	 * @see Execute
	 * @see Memory
	 * @see WriteBack
	 * @see MemoryType
	 * @see Listboxes
	 */
	public void step(Vector Instructions, MemoryType regFile,
			Memory myMemory, WriteBack myWriteBack,
			Fetch myFetch, Execute myExecute, DispInfo lb,Stages _stg) {

		String str;
		stg=_stg;
		this.RAWHazards=false;
		this.WARHazards=false;
		this.WAWHazards=false;
		Registers reg=Registers.getInstance();

		///this.myExec=myExecute;
		//this.myFetch=myFetch;
		//this.myWriteback=myWriteBack;
		// if last instruction was stalled, recall stalled instruction

		if (PC==Instructions.size())
			PC = -9;  // this is actually a NOP received from Fetch

		/*
		 * if it stalled then dont take the new instruction which is obviously NOP. Instead load the saved instruction
		 */
		if (isStall == true) {
			myInstruction = instructionSave;
			PC = savePC;
		}

		//--Check for RAW hazards in pipeline; ignore NOP instructions-----

		// check RAW hazard for rs

		stallflag = false;

		/*if ( ( ( (myExecute.myInstruction.opcode != 0) &&
				(myExecute.myInstruction.flush == false) &&
				(myInstruction.rs==
				((Integer) hazardList.elementAt(0) ).intValue() )) ||
				( (myMemory.myInstruction.opcode != 0) &&
						(myMemory.myInstruction.flush == false) &&
						(myInstruction.rs==
						((Integer) hazardList.elementAt(1) ).intValue() )) ||
						( (myWriteBack.myInstruction.opcode != 0) &&
								(myWriteBack.myInstruction.flush == false) &&
								(myInstruction.rs==
								((Integer) hazardList.elementAt(2) ).intValue() )) ) &&
								(myInstruction.opcode != 0) && (myInstruction.flush == false) )
		{
			stallflag = true;
		}*/

		/**
		 * find the hazards check if any register which is being included in the current instruction is busy now
		 */

		int iteration=myInstruction.getIteration_index()*Instructions.size();
		tablechart=OutputChart.getInstance();
		if((myInstruction.rdtype==regType.R && reg.isRegisterBusyR(myInstruction.rd))
				||(myInstruction.rttype==regType.R && reg.isRegisterBusyR(myInstruction.rt))
				||(myInstruction.rstype==regType.R && reg.isRegisterBusyR(myInstruction.rs))
				||(myInstruction.rdtype==regType.F && reg.isRegisterBusyF(myInstruction.rd))
				||(myInstruction.rttype==regType.F && reg.isRegisterBusyF(myInstruction.rt))
				||(myInstruction.rstype==regType.F && reg.isRegisterBusyF(myInstruction.rs)))
		{
			//stallflag=true;
			/**
			 * RAW hazards rs or rt are in write mode
			 * WAW hazards rd in write mode
			 * WAR hazards rd in read mode
			 */
			
			if((myInstruction.rstype==regType.R && reg.getRMode(myInstruction.rs)==registerMode.Write &&reg.isRegisterBusyR(myInstruction.rs))
					||(myInstruction.rstype==regType.F && reg.getFMode(myInstruction.rs)==registerMode.Write &&reg.isRegisterBusyF(myInstruction.rs))
					||(myInstruction.rttype==regType.R && reg.getRMode(myInstruction.rt)==registerMode.Write && reg.isRegisterBusyR(myInstruction.rt))
					||(myInstruction.rttype==regType.F && reg.getFMode(myInstruction.rt)==registerMode.Write &&reg.isRegisterBusyF(myInstruction.rt)))
			{
				//RAW hazards
				this.RAWHazards=true;
				stallflag=true;
				//tablechart=OutputChart.getInstance();
				tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 5, "Y");
			}
			if((myInstruction.rdtype==regType.R && reg.getRMode(myInstruction.rd)==registerMode.Read && reg.isRegisterBusyR(myInstruction.rd))
					||(myInstruction.rdtype==regType.F && reg.getFMode(myInstruction.rd)==registerMode.Read && reg.isRegisterBusyF(myInstruction.rd)))
			{
				//WAR hazards
				this.WARHazards=true;
				stallflag=true;
				//tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 6, "Y");
			}
			if((myInstruction.rdtype==regType.R && reg.getRMode(myInstruction.rd)==registerMode.Write && reg.isRegisterBusyR(myInstruction.rd))
					||(myInstruction.rdtype==regType.F && reg.getFMode(myInstruction.rd)==registerMode.Write && reg.isRegisterBusyF(myInstruction.rd)))
			{
				//WAW hazards
				this.WAWHazards=true;
				stallflag=true;
				tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 7, "Y");
			}
			
		}

		/**
		 * handle HLT instruction
		 * 
		 */

		if(myInstruction.opcode==82 &&myInstruction.getInstruction_index()>0)
		{
			//myInstruction.getInstruction_index();
			Instruction prevInstructioninlist=(Instruction)Instructions.elementAt(myInstruction.getInstruction_index()-1);
			if(prevInstructioninlist.opcode==82){
				myInstruction.flush=true;
				//System.exit(1);
			}

		}		// Harards detection process ends

		FunctionalUnitSingleton funit=FunctionalUnitSingleton.getInstance();

		//		if((myInstruction.opcode==24||myInstruction.opcode==26||myInstruction.opcode==32
		//				||myInstruction.opcode==34||myInstruction.opcode==40||myInstruction.opcode==42
		//				||myInstruction.opcode==44||myInstruction.opcode==46)
		//				&& (funit.getIntUnit().size()>0))
		//		{
		//			stallflag=true;
		//		}

		if(myInstruction.opcode==36 && funit.getFpAdder().size()>=funit.getFpAdderSize())
		{
			stallflag=true;
		}
		if(myInstruction.opcode==38 && funit.getFpMultiplier().size()>=funit.getFpMultiplierSize())
		{
			stallflag=true;
		}
		if(myInstruction.opcode==50 && funit.getFpDivider().size()>=funit.getFpDividerSize())
		{
			stallflag=true;
		}
		if(myInstruction.opcode==51 && funit.getFpAdder().size()>=funit.getFpAdderSize())
		{
			stallflag=true;
		}
		/*else if((myInstruction.opcode==48 ||myInstruction.opcode==54 ||myInstruction.opcode==56||myInstruction.opcode==58) && funit.getIntUnit2().size()>=funit.getIntUnit2Size())
		{
			stallflag=true;
		}*/

		//24,34,26,32,40,42,44,46, int
		//48,54,56,58 ld or sw
		//36,38,50,51 add.d, mult.d div.d sub.d
		// check for forwarding if hazard detected for rs

		/**
		 * if previous instruction and current instruciton opcode are same then check whether the previous will make the functional unit busy
		 *
		 */
		if(myInstruction.opcode!=0 && prevInstruction.opcode==myInstruction.opcode )
		{

			if(myInstruction.opcode==36 && funit.getFpAdder().size()>=funit.getFpAdderSize()-1)
			{
				stallflag=true;
			}
			else if(myInstruction.opcode==38 && funit.getFpMultiplier().size()>=funit.getFpMultiplierSize()-1)
			{
				stallflag=true;
			}
			else if(myInstruction.opcode==50 && funit.getFpDivider().size()>=funit.getFpDividerSize()-1)
			{
				stallflag=true;
			}
			else if(myInstruction.opcode==51 && funit.getFpAdder().size()>=funit.getFpAdderSize()-1)
			{
				stallflag=true;
			}


		}
		if (stallflag == true) {
			// RAW hazard found, so issue a stall
			if (isStall == false) {
				isStall = true;
				instructionSave = myInstruction;

				savePC = PC;
			}
			myInstruction = new Instruction("NOP");
			myInstruction.rsValue = 0;
			myInstruction.rtValue = 0;
			PC = -9;
		} else {
			// no new hazards; check to see if last instruction stalled, and clean up if so
			if (isStall == true) {
				isStall = false;
				myInstruction = instructionSave;

				/**
				 * test if jump occures
				 *
				 */

				if(myInstruction.opcode==81){
					Utilities utils=Utilities.getInstance();
					OutputChart chart=OutputChart.getInstance();

					int instructionid=utils.getInstructionForLabel(myInstruction.getLoopLabel());
					//Registers reg=Registers.getInstance();
					myFetch.PC = instructionid-1; // next instruct will be br addr
					myFetch.myInstruction.flush = true;
					myInstruction.flush = true;
					myInstruction=new Instruction("NOP");
					utils.setLoopiteration(utils.getLoopiteration()+1);
					chart.increasesizefornextiteration(utils.getLoopiteration());
				}
				if(myInstruction.opcode==82){ // HLT



					if(myFetch.myInstruction.opcode==82)
						System.exit(1);
				}
				if(myInstruction.opcode==70 || myInstruction.opcode==71){
					//case 70: // BEQ
					//case 71: // BNE
					utils=Utilities.getInstance();
					OutputChart chart=OutputChart.getInstance();
					int instructionid=utils.getInstructionForLabel(myInstruction.getLoopLabel());
					//Registers reg=Registers.getInstance();


					if (((myInstruction.opcode == 71) && (reg.readRegisterR(myInstruction.rd) != reg.readRegisterR(myInstruction.rs)))
							|| ((myInstruction.opcode == 70)&&(reg.readRegisterR(myInstruction.rd) == reg.readRegisterR(myInstruction.rs)))){
						myFetch.PC = instructionid-1; // next instruct will be br addr
						myFetch.myInstruction.flush = true;
						//myInstruction.flush = true;
						utils.setLoopiteration(utils.getLoopiteration()+1);
						chart.increasesizefornextiteration(utils.getLoopiteration());
						//	tableincrease=true;
						//myFetch.myInstruction.flush=true;
					}
				}
				/**
				 * update the fetch part while the decode of current on finished, as well the fetch is finished for the first one
				 */

				iteration=myInstruction.getIteration_index()*Instructions.size();
				tablechart=OutputChart.getInstance();
				// this is for fetch update, so add 1, next instruction fetch
				tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1+1, 1, Integer.toString(stg.nCycles));
				//System.out.println("Instruciton:"+myInstruction.getInstruction_index()+": Decoded at="+stg.nCycles);


			}
			/*first decode does not follow the stall)
			 *
			 */
			if(!myInstruction.instructionString.equals("NOP") && !myInstruction.flush)
			{
				holdRegisters();
				iteration=myInstruction.getIteration_index()*Instructions.size();
				/**
				 * if table increase in this iteration, then still you have to print the previous line not new line
				 */

				tablechart=OutputChart.getInstance();
				tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 2, Integer.toString(stg.nCycles));
				//System.out.println("Instruciton:"+myInstruction.getInstruction_index()+": Decoded at="+stg.nCycles);
			}

		}

		if (isStall == true) {
			// throw event to tell user a stall has been issued
			str = "Stall issued for instruction "+savePC+".";
			lb.tobeDisplay.add(str, 0);
		}

		/**
		 * save previous instruction for checking of consecutive same instruction
		 */
		prevInstruction=myInstruction;
	}

	public void holdRegisters()
	{
		/**
		 *REGISTER FORMAT
		 * LW OR SW DESTINATION VALUE(SOURCE) AND DEFINED BY rd rs and rt
		 * DADDI F1,F2,4 rd=F1, rs=F2
		 * ADD.D F1,F2,F3 F1=rd,rs=F2, rt=F3
		 */

		Registers reg=Registers.getInstance();
		if((myInstruction.opcode==24||myInstruction.opcode==26||myInstruction.opcode==32
				||myInstruction.opcode==34||myInstruction.opcode==36||myInstruction.opcode==38
				||myInstruction.opcode==50||myInstruction.opcode==51))
		{
			//DSUB=24,DADD=26,AND=32 OR=34 ADD.D=36 MUL.D=38 DIV.D=50 SUB.D=51
			if(myInstruction.rdtype==regType.R){
				reg.setIsRegisterBusyR(myInstruction.rd, true);
				reg.setRMode(myInstruction.rd, registerMode.Write);
			}
			else 
			{
				reg.setIsRegisterBusyF(myInstruction.rd, true);
				reg.setFMode(myInstruction.rd, registerMode.Write);
			}

			if(myInstruction.rstype==regType.R && reg.getRMode(myInstruction.rs)!=registerMode.Write)
			{
				reg.setIsRegisterBusyR(myInstruction.rs, false);
				
				reg.setRMode(myInstruction.rs, registerMode.Read);
			}
			else if(reg.getFMode(myInstruction.rs)!=registerMode.Write)
			{
				reg.setIsRegisterBusyF(myInstruction.rs, false);
				reg.setFMode(myInstruction.rs, registerMode.Read);
			}


			if(myInstruction.rttype==regType.R && reg.getRMode(myInstruction.rt)!=registerMode.Write)
			{
				reg.setIsRegisterBusyR(myInstruction.rt, false);
				reg.setRMode(myInstruction.rt, registerMode.Read);
			}
			else if(reg.getFMode(myInstruction.rt)!=registerMode.Write)
			{
				reg.setFMode(myInstruction.rt, registerMode.Read);
				reg.setIsRegisterBusyF(myInstruction.rt, false);
			}
		}
		else if(myInstruction.opcode==40||myInstruction.opcode==42
				||myInstruction.opcode==44||myInstruction.opcode==46)
		{
			//DADDI=40 DSUBI=42 ANDI=44 ORI=46
			if(myInstruction.rdtype==regType.R)
			{
				reg.setRMode(myInstruction.rd, registerMode.Write);
			
				reg.setIsRegisterBusyR(myInstruction.rd, true);
			}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rd, true);
				reg.setFMode(myInstruction.rd, registerMode.Write);
				}

			if(myInstruction.rstype==regType.R && reg.getRMode(myInstruction.rs)!=registerMode.Write)
			{
				reg.setIsRegisterBusyR(myInstruction.rs, false);
				reg.setRMode(myInstruction.rs, registerMode.Read);
			}
			else if(reg.getFMode(myInstruction.rs)!=registerMode.Write)
				{
				reg.setFMode(myInstruction.rs, registerMode.Read);
				reg.setIsRegisterBusyF(myInstruction.rs, false);
				}


		}
		else if(myInstruction.opcode==48||myInstruction.opcode==54
				||myInstruction.opcode==56||myInstruction.opcode==58)
		{
			//LW=48 SW=54 L.D=56 S.W=58
			if(myInstruction.rdtype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rd, true);
				reg.setRMode(myInstruction.rd, registerMode.Write);
			}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rd, true);
				reg.setFMode(myInstruction.rd, registerMode.Write);
				}



			if(myInstruction.rttype==regType.R && reg.getRMode(myInstruction.rt)!=registerMode.Write)
			{
				reg.setIsRegisterBusyR(myInstruction.rt, false);
				reg.setRMode(myInstruction.rt, registerMode.Read);
			}
			else if(reg.getFMode(myInstruction.rt)!=registerMode.Write)
				{
				reg.setIsRegisterBusyF(myInstruction.rt, false);
				reg.setFMode(myInstruction.rt, registerMode.Read);
				}
		}

		//immadiet

		//24,34,26,32,40,42,44,46, int
		//48,54,56,58 ld or sw
		//36,38,50,51 add.d, mult.d div.d sub.d

	}
	/**
	 * returns a string representation of the stage's current instruction and the results of its processing.
	 */
	public String toString() {
		String temp;

		if (myInstruction.flush == true) {
			temp = "FLUSHED: \n" + myInstruction + "\n";
			return temp;
		}

		if (PC >= 0) {
			temp = Integer.toString(PC) + ":\n" + myInstruction + "\n" +
					"ALUop1= " + Integer.toString(myInstruction.rsValue) + "\n" +
					"ALUop2= " + Integer.toString(myInstruction.rtValue) + "\n";
		} else {
			temp = myInstruction + "\n";
			if (isStall)
				temp += "Stalled:\n" + Integer.toString(savePC) + ":  " +
						instructionSave + "\n";
		}

		return temp;
	}

}

